import functools
from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for, jsonify
)
from werkzeug.security import check_password_hash, generate_password_hash
from flaskr.db import get_db


bp = Blueprint('user', __name__, url_prefix='/user')

def is_login_as_super():
    return is_login("admin")


def is_login(username=None):
    if g.user is None:
        return False
    elif username is not None:
        return g.user['username'] == username
    else:
        return True


def get_users():
    users = []
    rs = get_db().execute('SELECT * FROM users').fetchall()
    for r in rs:
        users.append(r['username'])
    return users


def get_logged_username():
    return g.user['username']


def login_required(view):
    @functools.wraps(view)
    def wrapped_view(*args, **kwargs):
        if not is_login():
            return redirect(url_for('user.entry'))
        return view(*args, **kwargs)

    return wrapped_view


def login_required_ret(nologin=None):
    def login_required_decorator(view):
        @functools.wraps(view)
        def wrapped_view(*args, **kwargs):
            if not is_login():
                return nologin if nologin is not None else redirect(url_for('user.entry'))
            return view(*args, **kwargs)
        return wrapped_view

    return login_required_decorator

def login_required_as_super_ret(nologin=None):
    def login_required_decorator(view):
        @functools.wraps(view)
        def wrapped_view(*args, **kwargs):
            if not is_login_as_super():
                return nologin if nologin is not None else redirect(url_for('user.entry'))
            return view(*args, **kwargs)
        return wrapped_view

    return login_required_decorator

def login_required_as_super(view):
    @functools.wraps(view)
    def wrapped_view(*args, **kwargs):
        if not is_login_as_super():
            return redirect(url_for('user.entry'))
        return view(*args, **kwargs)

    return wrapped_view


@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')
    if user_id is None:
        g.user = None
    else:
        g.user = get_db().execute('SELECT * FROM users WHERE id = ?', (user_id,)).fetchone()


@bp.route('/', methods=('GET', 'POST'))
@bp.route('/entry', methods=('GET', 'POST'))
def entry():
    if request.method == 'POST':
        username = request.form.get('username')
        action = request.form.get('action')
        password = request.form.get('password')
        old_password = request.form.get('old_password')
        new_password = request.form.get('new_password')
        db = get_db()

        if action == 'delete':
            if username is None or is_login(username):
                flash('Incorrect username.')
            else:
                db.execute('DELETE FROM users WHERE username = ?', (username,))
                db.commit()

        elif action == 'change_password':
            if username is None:
                flash('Incorrect username.')
            elif not password:
                flash('Password is required.')
            elif db.execute('SELECT id FROM users WHERE username = ?', (username,)).fetchone() is None:
                flash('User {} is not exist.'.format(username))
            else:
                db.execute('UPDATE users SET password=? WHERE username=?', (generate_password_hash(password), username))
                db.commit()
                flash('User {} changed password successfully.'.format(username))

        elif action == 'create':
            if not username:
                flash('Username is required.')
            elif not password:
                flash('Password is required.')
            elif db.execute('SELECT id FROM users WHERE username = ?', (username,)).fetchone() is not None:
                flash('User {} is already existed.'.format(username))
            else:
                db.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, generate_password_hash(password)))
                db.commit()
                flash('User {} created successfully.'.format(username))

        elif action == 'change_my_password':
            user = db.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()
            if username is None or username != g.user['username']:
                flash('Incorrect username.')
            elif not old_password or not check_password_hash(user['password'], old_password):
                flash('Incorrect old password.')
            elif not new_password:
                flash('A new password is needed.')
            else:
                db.execute('UPDATE users SET password=? WHERE username=?',
                           (generate_password_hash(new_password), username))
                db.commit()
                flash('User {} changed password successfully.'.format(username))

    users = get_users()
    return render_template('user/entry.html', super_user=is_login_as_super(), users=users)


@bp.route('/login', methods=('POST', 'GET'))
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        db = get_db()

        user = db.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone()

        if user is None:
            flash('Incorrect username.')
        elif not check_password_hash(user['password'], password):
            flash('Incorrect password.')
        else:
            session.clear()
            session['user_id'] = user['id']
            return redirect(url_for('index'))

    return render_template('user/login.html')


@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))


@bp.route('/simple_login', methods=('POST', ))
def simple_login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        db = get_db()

        user = db.execute(
            'SELECT * FROM users WHERE username = ?', (username,)
        ).fetchone()

        if user is None:
            return jsonify({
                "result": "failed",
                "error": "Incorrect username.",
                "account": username
            })
        elif not check_password_hash(user['password'], password):
            return jsonify({
                "result": "failed",
                "error": "Incorrect password.",
                "account": username
            })
        else:
            session.clear()
            session['user_id'] = user['id']
            session.clear()
            session['user_id'] = user['id']
            return jsonify({
                "result": "ok",
                "error": "None.",
                "account": username
            })

    return jsonify({
                    "result": "failed",
                    "error": "Not supported."
                })


@bp.route('/simple_logout')
def simple_logout():
    session.clear()
    return jsonify({
        "result": "ok",
        "error": "None."
    })
